<?php
/**
 * @Author: Wang chunsheng  email:2192138785@qq.com
 * @Date:   2020-08-05 16:01:22
 * @Last Modified by:   Wang chunsheng  email:2192138785@qq.com
 * @Last Modified time: 2020-08-05 16:08:04
 */

namespace common\helpers\soap;

 /**
 *
 * nusoap_server allows the user to create a SOAP server
 * that is capable of receiving messages and returning responses
 *
 * @author   Dietrich Ayala <dietrich@ganx4.com>
 * @author   Scott Nichol <snichol@users.sourceforge.net>
 * @version  $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $
 * @access   public
 */
class nusoap_server extends nusoap_base
{
    /**
     * HTTP headers of request
     *
     * @var array
     * @access private
     */
    var $headers = array();
    /**
     * HTTP request
     *
     * @var string
     * @access private
     */
    var $request = '';
    /**
     * SOAP headers from request (incomplete namespace resolution; special characters not escaped) (text)
     *
     * @var string
     * @access public
     */
    var $requestHeaders = '';
    /**
     * SOAP Headers from request (parsed)
     *
     * @var mixed
     * @access public
     */
    var $requestHeader = null;
    /**
     * SOAP body request portion (incomplete namespace resolution; special characters not escaped) (text)
     *
     * @var string
     * @access public
     */
    var $document = '';
    /**
     * SOAP payload for request (text)
     *
     * @var string
     * @access public
     */
    var $requestSOAP = '';
    /**
     * requested method namespace URI
     *
     * @var string
     * @access private
     */
    var $methodURI = '';
    /**
     * name of method requested
     *
     * @var string
     * @access private
     */
    var $methodname = '';
    /**
     * method parameters from request
     *
     * @var array
     * @access private
     */
    var $methodparams = array();
    /**
     * SOAP Action from request
     *
     * @var string
     * @access private
     */
    var $SOAPAction = '';
    /**
     * character set encoding of incoming (request) messages
     *
     * @var string
     * @access public
     */
    var $xml_encoding = '';
    /**
     * toggles whether the parser decodes element content w/ utf8_decode()
     *
     * @var boolean
     * @access public
     */
    var $decode_utf8 = true;

    /**
     * HTTP headers of response
     *
     * @var array
     * @access public
     */
    var $outgoing_headers = array();
    /**
     * HTTP response
     *
     * @var string
     * @access private
     */
    var $response = '';
    /**
     * SOAP headers for response (text or array of soapval or associative array)
     *
     * @var mixed
     * @access public
     */
    var $responseHeaders = '';
    /**
     * SOAP payload for response (text)
     *
     * @var string
     * @access private
     */
    var $responseSOAP = '';
    /**
     * method return value to place in response
     *
     * @var mixed
     * @access private
     */
    var $methodreturn = false;
    /**
     * whether $methodreturn is a string of literal XML
     *
     * @var boolean
     * @access public
     */
    var $methodreturnisliteralxml = false;
    /**
     * SOAP fault for response (or false)
     *
     * @var mixed
     * @access private
     */
    var $fault = false;
    /**
     * text indication of result (for debugging)
     *
     * @var string
     * @access private
     */
    var $result = 'successful';

    /**
     * assoc array of operations => opData; operations are added by the register()
     * method or by parsing an external WSDL definition
     *
     * @var array
     * @access private
     */
    var $operations = array();
    /**
     * wsdl instance (if one)
     *
     * @var mixed
     * @access private
     */
    var $wsdl = false;
    /**
     * URL for WSDL (if one)
     *
     * @var mixed
     * @access private
     */
    var $externalWSDLURL = false;
    /**
     * whether to append debug to response as XML comment
     *
     * @var boolean
     * @access public
     */
    var $debug_flag = false;


    /**
     * constructor
     * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to.
     *
     * @param mixed $wsdl file path or URL (string), or wsdl instance (object)
     * @access   public
     */
    function __construct($wsdl = false)
    {
        parent::__construct();
        // turn on debugging?
        global $debug;
        global $HTTP_SERVER_VARS;

        if (isset($_SERVER)) {
            $this->debug("_SERVER is defined:");
            $this->appendDebug($this->varDump($_SERVER));
        } elseif (isset($HTTP_SERVER_VARS)) {
            $this->debug("HTTP_SERVER_VARS is defined:");
            $this->appendDebug($this->varDump($HTTP_SERVER_VARS));
        } else {
            $this->debug("Neither _SERVER nor HTTP_SERVER_VARS is defined.");
        }

        if (isset($debug)) {
            $this->debug("In nusoap_server, set debug_flag=$debug based on global flag");
            $this->debug_flag = $debug;
        } elseif (isset($_SERVER['QUERY_STRING'])) {
            $qs = explode('&', $_SERVER['QUERY_STRING']);
            foreach ($qs as $v) {
                if (substr($v, 0, 6) == 'debug=') {
                    $this->debug("In nusoap_server, set debug_flag=" . substr($v, 6) . " based on query string #1");
                    $this->debug_flag = substr($v, 6);
                }
            }
        } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
            $qs = explode('&', $HTTP_SERVER_VARS['QUERY_STRING']);
            foreach ($qs as $v) {
                if (substr($v, 0, 6) == 'debug=') {
                    $this->debug("In nusoap_server, set debug_flag=" . substr($v, 6) . " based on query string #2");
                    $this->debug_flag = substr($v, 6);
                }
            }
        }

        // wsdl
        if ($wsdl) {
            $this->debug("In nusoap_server, WSDL is specified");
            if (is_object($wsdl) && (get_class($wsdl) == 'wsdl')) {
                $this->wsdl = $wsdl;
                $this->externalWSDLURL = $this->wsdl->wsdl;
                $this->debug('Use existing wsdl instance from ' . $this->externalWSDLURL);
            } else {
                $this->debug('Create wsdl from ' . $wsdl);
                $this->wsdl = new wsdl($wsdl);
                $this->externalWSDLURL = $wsdl;
            }
            $this->appendDebug($this->wsdl->getDebug());
            $this->wsdl->clearDebug();
            if ($err = $this->wsdl->getError()) {
                die('WSDL ERROR: ' . $err);
            }
        }
    }

    /**
     * processes request and returns response
     *
     * @param    string $data usually is the value of $HTTP_RAW_POST_DATA
     * @access   public
     */
    function service($data)
    {
        global $HTTP_SERVER_VARS;

        if (isset($_SERVER['REQUEST_METHOD'])) {
            $rm = $_SERVER['REQUEST_METHOD'];
        } elseif (isset($HTTP_SERVER_VARS['REQUEST_METHOD'])) {
            $rm = $HTTP_SERVER_VARS['REQUEST_METHOD'];
        } else {
            $rm = '';
        }

        if (isset($_SERVER['QUERY_STRING'])) {
            $qs = $_SERVER['QUERY_STRING'];
        } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) {
            $qs = $HTTP_SERVER_VARS['QUERY_STRING'];
        } else {
            $qs = '';
        }
        $this->debug("In service, request method=$rm query string=$qs strlen(\$data)=" . strlen($data));

        if ($rm == 'POST') {
            $this->debug("In service, invoke the request");
            $this->parse_request($data);
            if (!$this->fault) {
                $this->invoke_method();
            }
            if (!$this->fault) {
                $this->serialize_return();
            }
            $this->send_response();
        } elseif (preg_match('/wsdl/', $qs)) {
            $this->debug("In service, this is a request for WSDL");
            if ($this->externalWSDLURL) {
                if (strpos($this->externalWSDLURL, "http://") !== false) { // assume URL
                    $this->debug("In service, re-direct for WSDL");
                    header('Location: ' . $this->externalWSDLURL);
                } else { // assume file
                    $this->debug("In service, use file passthru for WSDL");
                    header("Content-Type: text/xml\r\n");
                    $pos = strpos($this->externalWSDLURL, "file://");
                    if ($pos === false) {
                        $filename = $this->externalWSDLURL;
                    } else {
                        $filename = substr($this->externalWSDLURL, $pos + 7);
                    }
                    $fp = fopen($this->externalWSDLURL, 'r');
                    fpassthru($fp);
                }
            } elseif ($this->wsdl) {
                $this->debug("In service, serialize WSDL");
                header("Content-Type: text/xml; charset=ISO-8859-1\r\n");
                print $this->wsdl->serialize($this->debug_flag);
                if ($this->debug_flag) {
                    $this->debug('wsdl:');
                    $this->appendDebug($this->varDump($this->wsdl));
                    print $this->getDebugAsXMLComment();
                }
            } else {
                $this->debug("In service, there is no WSDL");
                header("Content-Type: text/html; charset=ISO-8859-1\r\n");
                print "This service does not provide WSDL";
            }
        } elseif ($this->wsdl) {
            $this->debug("In service, return Web description");
            print $this->wsdl->webDescription();
        } else {
            $this->debug("In service, no Web description");
            header("Content-Type: text/html; charset=ISO-8859-1\r\n");
            print "This service does not provide a Web description";
        }
    }

    /**
     * parses HTTP request headers.
     *
     * The following fields are set by this function (when successful)
     *
     * headers
     * request
     * xml_encoding
     * SOAPAction
     *
     * @access   private
     */
    function parse_http_headers()
    {
        global $HTTP_SERVER_VARS;

        $this->request = '';
        $this->SOAPAction = '';
        if (function_exists('getallheaders')) {
            $this->debug("In parse_http_headers, use getallheaders");
            $headers = getallheaders();
            foreach ($headers as $k => $v) {
                $k = strtolower($k);
                $this->headers[$k] = $v;
                $this->request .= "$k: $v\r\n";
                $this->debug("$k: $v");
            }
            // get SOAPAction header
            if (isset($this->headers['soapaction'])) {
                $this->SOAPAction = str_replace('"', '', $this->headers['soapaction']);
            }
            // get the character encoding of the incoming request
            if (isset($this->headers['content-type']) && strpos($this->headers['content-type'], '=')) {
                $enc = str_replace('"', '', substr(strstr($this->headers["content-type"], '='), 1));
                if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) {
                    $this->xml_encoding = strtoupper($enc);
                } else {
                    $this->xml_encoding = 'US-ASCII';
                }
            } else {
                // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
                $this->xml_encoding = 'ISO-8859-1';
            }
        } elseif (isset($_SERVER) && is_array($_SERVER)) {
            $this->debug("In parse_http_headers, use _SERVER");
            foreach ($_SERVER as $k => $v) {
                if (substr($k, 0, 5) == 'HTTP_') {
                    $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5))));
                } else {
                    $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k)));
                }
                if ($k == 'soapaction') {
                    // get SOAPAction header
                    $k = 'SOAPAction';
                    $v = str_replace('"', '', $v);
                    $v = str_replace('\\', '', $v);
                    $this->SOAPAction = $v;
                } elseif ($k == 'content-type') {
                    // get the character encoding of the incoming request
                    if (strpos($v, '=')) {
                        $enc = substr(strstr($v, '='), 1);
                        $enc = str_replace('"', '', $enc);
                        $enc = str_replace('\\', '', $enc);
                        if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) {
                            $this->xml_encoding = strtoupper($enc);
                        } else {
                            $this->xml_encoding = 'US-ASCII';
                        }
                    } else {
                        // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
                        $this->xml_encoding = 'ISO-8859-1';
                    }
                }
                $this->headers[$k] = $v;
                if (is_array($v)) {
                    $this->request .= "$k: " . json_encode($v) . "\r\n";
                    $this->debug("$k: " . json_encode($v));
                } else {
                    $this->request .= "$k: $v\r\n";
                    $this->debug("$k: $v");
                }
            }
        } elseif (is_array($HTTP_SERVER_VARS)) {
            $this->debug("In parse_http_headers, use HTTP_SERVER_VARS");
            foreach ($HTTP_SERVER_VARS as $k => $v) {
                if (substr($k, 0, 5) == 'HTTP_') {
                    $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5))));
                    $k = strtolower(substr($k, 5));
                } else {
                    $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k)));
                    $k = strtolower($k);
                }
                if ($k == 'soapaction') {
                    // get SOAPAction header
                    $k = 'SOAPAction';
                    $v = str_replace('"', '', $v);
                    $v = str_replace('\\', '', $v);
                    $this->SOAPAction = $v;
                } elseif ($k == 'content-type') {
                    // get the character encoding of the incoming request
                    if (strpos($v, '=')) {
                        $enc = substr(strstr($v, '='), 1);
                        $enc = str_replace('"', '', $enc);
                        $enc = str_replace('\\', '', $enc);
                        if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) {
                            $this->xml_encoding = strtoupper($enc);
                        } else {
                            $this->xml_encoding = 'US-ASCII';
                        }
                    } else {
                        // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
                        $this->xml_encoding = 'ISO-8859-1';
                    }
                }
                $this->headers[$k] = $v;
                $this->request .= "$k: $v\r\n";
                $this->debug("$k: $v");
            }
        } else {
            $this->debug("In parse_http_headers, HTTP headers not accessible");
            $this->setError("HTTP headers not accessible");
        }
    }

    /**
     * parses a request
     *
     * The following fields are set by this function (when successful)
     *
     * headers
     * request
     * xml_encoding
     * SOAPAction
     * request
     * requestSOAP
     * methodURI
     * methodname
     * methodparams
     * requestHeaders
     * document
     *
     * This sets the fault field on error
     *
     * @param    string $data XML string
     * @access   private
     */
    function parse_request($data = '')
    {
        $this->debug('entering parse_request()');
        $this->parse_http_headers();
        $this->debug('got character encoding: ' . $this->xml_encoding);
        // uncompress if necessary
        if (isset($this->headers['content-encoding']) && $this->headers['content-encoding'] != '') {
            $this->debug('got content encoding: ' . $this->headers['content-encoding']);
            if ($this->headers['content-encoding'] == 'deflate' || $this->headers['content-encoding'] == 'gzip') {
                // if decoding works, use it. else assume data wasn't gzencoded
                if (function_exists('gzuncompress')) {
                    if ($this->headers['content-encoding'] == 'deflate' && $degzdata = @gzuncompress($data)) {
                        $data = $degzdata;
                    } elseif ($this->headers['content-encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))) {
                        $data = $degzdata;
                    } else {
                        $this->fault('SOAP-ENV:Client', 'Errors occurred when trying to decode the data');
                        return;
                    }
                } else {
                    $this->fault('SOAP-ENV:Client', 'This Server does not support compressed data');
                    return;
                }
            }
        }
        $this->request .= "\r\n" . $data;
        $data = $this->parseRequest($this->headers, $data);
        $this->requestSOAP = $data;
        $this->debug('leaving parse_request');
    }

    /**
     * invokes a PHP function for the requested SOAP method
     *
     * The following fields are set by this function (when successful)
     *
     * methodreturn
     *
     * Note that the PHP function that is called may also set the following
     * fields to affect the response sent to the client
     *
     * responseHeaders
     * outgoing_headers
     *
     * This sets the fault field on error
     *
     * @access   private
     */
    function invoke_method()
    {
        $this->debug('in invoke_method, methodname=' . $this->methodname . ' methodURI=' . $this->methodURI . ' SOAPAction=' . $this->SOAPAction);

        //
        // if you are debugging in this area of the code, your service uses a class to implement methods,
        // you use SOAP RPC, and the client is .NET, please be aware of the following...
        // when the .NET wsdl.exe utility generates a proxy, it will remove the '.' or '..' from the
        // method name.  that is fine for naming the .NET methods.  it is not fine for properly constructing
        // the XML request and reading the XML response.  you need to add the RequestElementName and
        // ResponseElementName to the System.Web.Services.Protocols.SoapRpcMethodAttribute that wsdl.exe
        // generates for the method.  these parameters are used to specify the correct XML element names
        // for .NET to use, i.e. the names with the '.' in them.
        //
        $orig_methodname = $this->methodname;
        if ($this->wsdl) {
            if ($this->opData = $this->wsdl->getOperationData($this->methodname)) {
                $this->debug('in invoke_method, found WSDL operation=' . $this->methodname);
                $this->appendDebug('opData=' . $this->varDump($this->opData));
            } elseif ($this->opData = $this->wsdl->getOperationDataForSoapAction($this->SOAPAction)) {
                // Note: hopefully this case will only be used for doc/lit, since rpc services should have wrapper element
                $this->debug('in invoke_method, found WSDL soapAction=' . $this->SOAPAction . ' for operation=' . $this->opData['name']);
                $this->appendDebug('opData=' . $this->varDump($this->opData));
                $this->methodname = $this->opData['name'];
            } else {
                $this->debug('in invoke_method, no WSDL for operation=' . $this->methodname);
                $this->fault('SOAP-ENV:Client', "Operation '" . $this->methodname . "' is not defined in the WSDL for this service");
                return;
            }
        } else {
            $this->debug('in invoke_method, no WSDL to validate method');
        }

        // if a . is present in $this->methodname, we see if there is a class in scope,
        // which could be referred to. We will also distinguish between two deliminators,
        // to allow methods to be called a the class or an instance
        if (strpos($this->methodname, '..') > 0) {
            $delim = '..';
        } elseif (strpos($this->methodname, '.') > 0) {
            $delim = '.';
        } else {
            $delim = '';
        }
        $this->debug("in invoke_method, delim=$delim");

        $class = '';
        $method = '';
        if (strlen($delim) > 0 && substr_count($this->methodname, $delim) == 1) {
            $try_class = substr($this->methodname, 0, strpos($this->methodname, $delim));
            if (class_exists($try_class)) {
                // get the class and method name
                $class = $try_class;
                $method = substr($this->methodname, strpos($this->methodname, $delim) + strlen($delim));
                $this->debug("in invoke_method, class=$class method=$method delim=$delim");
            } else {
                $this->debug("in invoke_method, class=$try_class not found");
            }
        } elseif (strlen($delim) > 0 && substr_count($this->methodname, $delim) > 1) {
            $split = explode($delim, $this->methodname);
            $method = array_pop($split);
            $class = implode('\\', $split);
        } else {
            $try_class = '';
            $this->debug("in invoke_method, no class to try");
        }

        // does method exist?
        if ($class == '') {
            if (!function_exists($this->methodname)) {
                $this->debug("in invoke_method, function '$this->methodname' not found!");
                $this->result = 'fault: method not found';
                $this->fault('SOAP-ENV:Client', "method '$this->methodname'('$orig_methodname') not defined in service('$try_class' '$delim')");
                return;
            }
        } else {
            $method_to_compare = (substr(phpversion(), 0, 2) == '4.') ? strtolower($method) : $method;
            if (!in_array($method_to_compare, get_class_methods($class))) {
                $this->debug("in invoke_method, method '$this->methodname' not found in class '$class'!");
                $this->result = 'fault: method not found';
                $this->fault('SOAP-ENV:Client', "method '$this->methodname'/'$method_to_compare'('$orig_methodname') not defined in service/'$class'('$try_class' '$delim')");
                return;
            }
        }

        // evaluate message, getting back parameters
        // verify that request parameters match the method's signature
        if (!$this->verify_method($this->methodname, $this->methodparams)) {
            // debug
            $this->debug('ERROR: request not verified against method signature');
            $this->result = 'fault: request failed validation against method signature';
            // return fault
            $this->fault('SOAP-ENV:Client', "Operation '$this->methodname' not defined in service.");
            return;
        }

        // if there are parameters to pass
        $this->debug('in invoke_method, params:');
        $this->appendDebug($this->varDump($this->methodparams));
        $this->debug("in invoke_method, calling '$this->methodname'");
        if (!function_exists('call_user_func_array')) {
            if ($class == '') {
                $this->debug('in invoke_method, calling function using eval()');
                $funcCall = "\$this->methodreturn = $this->methodname(";
            } else {
                if ($delim == '..') {
                    $this->debug('in invoke_method, calling class method using eval()');
                    $funcCall = "\$this->methodreturn = " . $class . "::" . $method . "(";
                } else {
                    $this->debug('in invoke_method, calling instance method using eval()');
                    // generate unique instance name
                    $instname = "\$inst_" . time();
                    $funcCall = $instname . " = new " . $class . "(); ";
                    $funcCall .= "\$this->methodreturn = " . $instname . "->" . $method . "(";
                }
            }
            if ($this->methodparams) {
                foreach ($this->methodparams as $param) {
                    if (is_array($param) || is_object($param)) {
                        $this->fault('SOAP-ENV:Client', 'NuSOAP does not handle complexType parameters correctly when using eval; call_user_func_array must be available');
                        return;
                    }
                    $funcCall .= "\"$param\",";
                }
                $funcCall = substr($funcCall, 0, -1);
            }
            $funcCall .= ');';
            $this->debug('in invoke_method, function call: ' . $funcCall);
            @eval($funcCall);
        } else {
            if ($class == '') {
                $this->debug('in invoke_method, calling function using call_user_func_array()');
                $call_arg = "$this->methodname";    // straight assignment changes $this->methodname to lower case after call_user_func_array()
            } elseif ($delim == '..') {
                $this->debug('in invoke_method, calling class method using call_user_func_array()');
                $call_arg = array($class, $method);
            } else {
                $this->debug('in invoke_method, calling instance method using call_user_func_array()');
                $instance = new $class ();
                $call_arg = array(&$instance, $method);
            }
            if (is_array($this->methodparams)) {
                $this->methodreturn = call_user_func_array($call_arg, array_values($this->methodparams));
            } else {
                $this->methodreturn = call_user_func_array($call_arg, array());
            }
        }
        $this->debug('in invoke_method, methodreturn:');
        $this->appendDebug($this->varDump($this->methodreturn));
        $this->debug("in invoke_method, called method $this->methodname, received data of type " . gettype($this->methodreturn));
    }

    /**
     * serializes the return value from a PHP function into a full SOAP Envelope
     *
     * The following fields are set by this function (when successful)
     *
     * responseSOAP
     *
     * This sets the fault field on error
     *
     * @access   private
     */
    function serialize_return()
    {
        $this->debug('Entering serialize_return methodname: ' . $this->methodname . ' methodURI: ' . $this->methodURI);
        // if fault
        if (isset($this->methodreturn) && is_object($this->methodreturn) && ((get_class($this->methodreturn) == 'soap_fault') || (get_class($this->methodreturn) == 'nusoap_fault'))) {
            $this->debug('got a fault object from method');
            $this->fault = $this->methodreturn;
            return;
        } elseif ($this->methodreturnisliteralxml) {
            $return_val = $this->methodreturn;
            // returned value(s)
        } else {
            $this->debug('got a(n) ' . gettype($this->methodreturn) . ' from method');
            $this->debug('serializing return value');
            if ($this->wsdl) {
                if (sizeof($this->opData['output']['parts']) > 1) {
                    $this->debug('more than one output part, so use the method return unchanged');
                    $opParams = $this->methodreturn;
                } elseif (sizeof($this->opData['output']['parts']) == 1) {
                    $this->debug('exactly one output part, so wrap the method return in a simple array');
                    // TODO: verify that it is not already wrapped!
                    //foreach ($this->opData['output']['parts'] as $name => $type) {
                    //	$this->debug('wrap in element named ' . $name);
                    //}
                    $opParams = array($this->methodreturn);
                }
                $opParams = isset($opParams) ? $opParams : [];
                $return_val = $this->wsdl->serializeRPCParameters($this->methodname, 'output', $opParams);
                $this->appendDebug($this->wsdl->getDebug());
                $this->wsdl->clearDebug();
                if ($errstr = $this->wsdl->getError()) {
                    $this->debug('got wsdl error: ' . $errstr);
                    $this->fault('SOAP-ENV:Server', 'unable to serialize result');
                    return;
                }
            } else {
                if (isset($this->methodreturn)) {
                    $return_val = $this->serialize_val($this->methodreturn, 'return');
                } else {
                    $return_val = '';
                    $this->debug('in absence of WSDL, assume void return for backward compatibility');
                }
            }
        }
        $this->debug('return value:');
        $this->appendDebug($this->varDump($return_val));

        $this->debug('serializing response');
        if ($this->wsdl) {
            $this->debug('have WSDL for serialization: style is ' . $this->opData['style']);
            if ($this->opData['style'] == 'rpc') {
                $this->debug('style is rpc for serialization: use is ' . $this->opData['output']['use']);
                if ($this->opData['output']['use'] == 'literal') {
                    // http://www.ws-i.org/Profiles/BasicProfile-1.1-2004-08-24.html R2735 says rpc/literal accessor elements should not be in a namespace
                    if ($this->methodURI) {
                        $payload = '<ns1:' . $this->methodname . 'Response xmlns:ns1="' . $this->methodURI . '">' . $return_val . '</ns1:' . $this->methodname . "Response>";
                    } else {
                        $payload = '<' . $this->methodname . 'Response>' . $return_val . '</' . $this->methodname . 'Response>';
                    }
                } else {
                    if ($this->methodURI) {
                        $payload = '<ns1:' . $this->methodname . 'Response xmlns:ns1="' . $this->methodURI . '">' . $return_val . '</ns1:' . $this->methodname . "Response>";
                    } else {
                        $payload = '<' . $this->methodname . 'Response>' . $return_val . '</' . $this->methodname . 'Response>';
                    }
                }
            } else {
                $this->debug('style is not rpc for serialization: assume document');
                $payload = $return_val;
            }
        } else {
            $this->debug('do not have WSDL for serialization: assume rpc/encoded');
            $payload = '<ns1:' . $this->methodname . 'Response xmlns:ns1="' . $this->methodURI . '">' . $return_val . '</ns1:' . $this->methodname . "Response>";
        }
        $this->result = 'successful';
        if ($this->wsdl) {
            //if($this->debug_flag){
            $this->appendDebug($this->wsdl->getDebug());
            //	}
            if (isset($this->opData['output']['encodingStyle'])) {
                $encodingStyle = $this->opData['output']['encodingStyle'];
            } else {
                $encodingStyle = '';
            }
            // Added: In case we use a WSDL, return a serialized env. WITH the usedNamespaces.
            $this->responseSOAP = $this->serializeEnvelope($payload, $this->responseHeaders, $this->wsdl->usedNamespaces, $this->opData['style'], $this->opData['output']['use'], $encodingStyle);
        } else {
            $this->responseSOAP = $this->serializeEnvelope($payload, $this->responseHeaders);
        }
        $this->debug("Leaving serialize_return");
    }

    /**
     * sends an HTTP response
     *
     * The following fields are set by this function (when successful)
     *
     * outgoing_headers
     * response
     *
     * @access   private
     */
    function send_response()
    {
        $this->debug('Enter send_response');
        if ($this->fault) {
            $payload = $this->fault->serialize();
            $this->outgoing_headers[] = "HTTP/1.0 500 Internal Server Error";
            $this->outgoing_headers[] = "Status: 500 Internal Server Error";
        } else {
            $payload = $this->responseSOAP;
            // Some combinations of PHP+Web server allow the Status
            // to come through as a header.  Since OK is the default
            // just do nothing.
            // $this->outgoing_headers[] = "HTTP/1.0 200 OK";
            // $this->outgoing_headers[] = "Status: 200 OK";
        }
        // add debug data if in debug mode
        if (isset($this->debug_flag) && $this->debug_flag) {
            $payload .= $this->getDebugAsXMLComment();
        }
        $this->outgoing_headers[] = "Server: $this->title Server v$this->version";
        preg_match('/\$Revisio' . 'n: ([^ ]+)/', $this->revision, $rev);
        $this->outgoing_headers[] = "X-SOAP-Server: $this->title/$this->version (" . $rev[1] . ")";
        // Let the Web server decide about this
        //$this->outgoing_headers[] = "Connection: Close\r\n";
        $payload = $this->getHTTPBody($payload);
        $type = $this->getHTTPContentType();
        $charset = $this->getHTTPContentTypeCharset();
        $this->outgoing_headers[] = "Content-Type: $type" . ($charset ? '; charset=' . $charset : '');
        //begin code to compress payload - by John
        // NOTE: there is no way to know whether the Web server will also compress
        // this data.
        if (strlen($payload) > 1024 && isset($this->headers) && isset($this->headers['accept-encoding'])) {
            if (strstr($this->headers['accept-encoding'], 'gzip')) {
                if (function_exists('gzencode')) {
                    if (isset($this->debug_flag) && $this->debug_flag) {
                        $payload .= "<!-- Content being gzipped -->";
                    }
                    $this->outgoing_headers[] = "Content-Encoding: gzip";
                    $payload = gzencode($payload);
                } else {
                    if (isset($this->debug_flag) && $this->debug_flag) {
                        $payload .= "<!-- Content will not be gzipped: no gzencode -->";
                    }
                }
            } elseif (strstr($this->headers['accept-encoding'], 'deflate')) {
                // Note: MSIE requires gzdeflate output (no Zlib header and checksum),
                // instead of gzcompress output,
                // which conflicts with HTTP 1.1 spec (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.5)
                if (function_exists('gzdeflate')) {
                    if (isset($this->debug_flag) && $this->debug_flag) {
                        $payload .= "<!-- Content being deflated -->";
                    }
                    $this->outgoing_headers[] = "Content-Encoding: deflate";
                    $payload = gzdeflate($payload);
                } else {
                    if (isset($this->debug_flag) && $this->debug_flag) {
                        $payload .= "<!-- Content will not be deflated: no gzcompress -->";
                    }
                }
            }
        }
        //end code
        $this->outgoing_headers[] = "Content-Length: " . strlen($payload);
        reset($this->outgoing_headers);
        foreach ($this->outgoing_headers as $hdr) {
            header($hdr, false);
        }
        print $payload;
        $this->response = join("\r\n", $this->outgoing_headers) . "\r\n\r\n" . $payload;
    }

    /**
     * takes the value that was created by parsing the request
     * and compares to the method's signature, if available.
     *
     * @param    string $operation The operation to be invoked
     * @param    array $request The array of parameter values
     * @return    boolean    Whether the operation was found
     * @access   private
     */
    function verify_method($operation, $request)
    {
        if (isset($this->wsdl) && is_object($this->wsdl)) {
            if ($this->wsdl->getOperationData($operation)) {
                return true;
            }
        } elseif (isset($this->operations[$operation])) {
            return true;
        }
        return false;
    }

    /**
     * processes SOAP message received from client
     *
     * @param    array $headers The HTTP headers
     * @param    string $data unprocessed request data from client
     * @return    mixed    value of the message, decoded into a PHP type
     * @access   private
     */
    function parseRequest($headers, $data)
    {
        $this->debug('Entering parseRequest() for data of length ' . strlen($data) . ' headers:');
        $this->appendDebug($this->varDump($headers));
        if (!isset($headers['content-type'])) {
            $this->setError('Request not of type '.$this->contentType.' (no content-type header)');
            return false;
        }
        if (!strstr($headers['content-type'], $this->contentType)) {
            $this->setError('Request not of type '.$this->contentType.': ' . $headers['content-type']);
            return false;
        }
        if (strpos($headers['content-type'], '=')) {
            $enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1));
            $this->debug('Got response encoding: ' . $enc);
            if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i', $enc)) {
                $this->xml_encoding = strtoupper($enc);
            } else {
                $this->xml_encoding = 'US-ASCII';
            }
        } else {
            // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1
            $this->xml_encoding = 'ISO-8859-1';
        }
        $this->debug('Use encoding: ' . $this->xml_encoding . ' when creating nusoap_parser');
        // parse response, get soap parser obj
        $parser = new nusoap_parser($data, $this->xml_encoding, '', $this->decode_utf8);
        // parser debug
        $this->debug("parser debug: \n" . $parser->getDebug());
        // if fault occurred during message parsing
        if ($err = $parser->getError()) {
            $this->result = 'fault: error in msg parsing: ' . $err;
            $this->fault('SOAP-ENV:Client', "error in msg parsing:\n" . $err);
            // else successfully parsed request into soapval object
        } else {
            // get/set methodname
            $this->methodURI = $parser->root_struct_namespace;
            $this->methodname = $parser->root_struct_name;
            $this->debug('methodname: ' . $this->methodname . ' methodURI: ' . $this->methodURI);
            $this->debug('calling parser->get_soapbody()');
            $this->methodparams = $parser->get_soapbody();
            // get SOAP headers
            $this->requestHeaders = $parser->getHeaders();
            // get SOAP Header
            $this->requestHeader = $parser->get_soapheader();
            // add document for doclit support
            $this->document = $parser->document;
        }
    }

    /**
     * gets the HTTP body for the current response.
     *
     * @param string $soapmsg The SOAP payload
     * @return string The HTTP body, which includes the SOAP payload
     * @access private
     */
    function getHTTPBody($soapmsg)
    {
        return $soapmsg;
    }

    /**
     * gets the HTTP content type for the current response.
     *
     * Note: getHTTPBody must be called before this.
     *
     * @return string the HTTP content type for the current response.
     * @access private
     */
    function getHTTPContentType()
    {
        return 'text/xml';
    }

    /**
     * gets the HTTP content type charset for the current response.
     * returns false for non-text content types.
     *
     * Note: getHTTPBody must be called before this.
     *
     * @return string the HTTP content type charset for the current response.
     * @access private
     */
    function getHTTPContentTypeCharset()
    {
        return $this->soap_defencoding;
    }

    /**
     * add a method to the dispatch map (this has been replaced by the register method)
     *
     * @param    string $methodname
     * @param    string $in array of input values
     * @param    string $out array of output values
     * @access   public
     * @deprecated
     */
    function add_to_map($methodname, $in, $out)
    {
        $this->operations[$methodname] = array('name' => $methodname, 'in' => $in, 'out' => $out);
    }

    /**
     * register a service function with the server
     *
     * @param    string $name the name of the PHP function, class.method or class..method
     * @param    array $in assoc array of input values: key = param name, value = param type
     * @param    array $out assoc array of output values: key = param name, value = param type
     * @param    mixed $namespace the element namespace for the method or false
     * @param    mixed $soapaction the soapaction for the method or false
     * @param    mixed $style optional (rpc|document) or false Note: when 'document' is specified, parameter and return wrappers are created for you automatically
     * @param    mixed $use optional (encoded|literal) or false
     * @param    string $documentation optional Description to include in WSDL
     * @param    string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded)
     * @access   public
     */
    function register($name, $in = array(), $out = array(), $namespace = false, $soapaction = false, $style = false, $use = false, $documentation = '', $encodingStyle = '')
    {
        global $HTTP_SERVER_VARS;

        if ($this->externalWSDLURL) {
            die('You cannot bind to an external WSDL file, and register methods outside of it! Please choose either WSDL or no WSDL.');
        }
        if (!$name) {
            die('You must specify a name when you register an operation');
        }
        if (!is_array($in)) {
            die('You must provide an array for operation inputs');
        }
        if (!is_array($out)) {
            die('You must provide an array for operation outputs');
        }
        if (false == $namespace) {
        }
        if (false == $soapaction) {
            if (isset($_SERVER)) {
                $SERVER_NAME = $_SERVER['SERVER_NAME'];
                $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
                $HTTPS = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : (isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off');
            } elseif (isset($HTTP_SERVER_VARS)) {
                $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME'];
                $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME'];
                $HTTPS = isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off';
            } else {
                $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
            }
            if ($HTTPS == '1' || $HTTPS == 'on') {
                $SCHEME = 'https';
            } else {
                $SCHEME = 'http';
            }
            $soapaction = "$SCHEME://$SERVER_NAME$SCRIPT_NAME/$name";
        }
        if (false == $style) {
            $style = "rpc";
        }
        if (false == $use) {
            $use = "encoded";
        }
        if ($use == 'encoded' && $encodingStyle == '') {
            $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/';
        }

        $this->operations[$name] = array(
            'name' => $name,
            'in' => $in,
            'out' => $out,
            'namespace' => $namespace,
            'soapaction' => $soapaction,
            'style' => $style);
        if ($this->wsdl) {
            $this->wsdl->addOperation($name, $in, $out, $namespace, $soapaction, $style, $use, $documentation, $encodingStyle);
        }
        return true;
    }

    /**
     * Specify a fault to be returned to the client.
     * This also acts as a flag to the server that a fault has occured.
     *
     * @param    string $faultcode
     * @param    string $faultstring
     * @param    string $faultactor
     * @param    string $faultdetail
     * @access   public
     */
    function fault($faultcode, $faultstring, $faultactor = '', $faultdetail = '')
    {
        if ($faultdetail == '' && $this->debug_flag) {
            $faultdetail = $this->getDebug();
        }
        $this->fault = new nusoap_fault($faultcode, $faultactor, $faultstring, $faultdetail);
        $this->fault->soap_defencoding = $this->soap_defencoding;
    }

    /**
     * Sets up wsdl object.
     * Acts as a flag to enable internal WSDL generation
     *
     * @param string $serviceName , name of the service
     * @param mixed $namespace optional 'tns' service namespace or false
     * @param mixed $endpoint optional URL of service endpoint or false
     * @param string $style optional (rpc|document) WSDL style (also specified by operation)
     * @param string $transport optional SOAP transport
     * @param mixed $schemaTargetNamespace optional 'types' targetNamespace for service schema or false
     */
    function configureWSDL($serviceName, $namespace = false, $endpoint = false, $style = 'rpc', $transport = 'http://schemas.xmlsoap.org/soap/http', $schemaTargetNamespace = false)
    {
        global $HTTP_SERVER_VARS;

        if (isset($_SERVER)) {
            $SERVER_NAME = $_SERVER['SERVER_NAME'];
            $SERVER_PORT = $_SERVER['SERVER_PORT'];
            $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
            $HTTPS = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : (isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off');
        } elseif (isset($HTTP_SERVER_VARS)) {
            $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME'];
            $SERVER_PORT = $HTTP_SERVER_VARS['SERVER_PORT'];
            $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME'];
            $HTTPS = isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off';
        } else {
            $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available");
        }
        // If server name has port number attached then strip it (else port number gets duplicated in WSDL output) (occurred using lighttpd and FastCGI)
        $colon = strpos($SERVER_NAME, ":");
        if ($colon) {
            $SERVER_NAME = substr($SERVER_NAME, 0, $colon);
        }
        if ($SERVER_PORT == 80) {
            $SERVER_PORT = '';
        } else {
            $SERVER_PORT = ':' . $SERVER_PORT;
        }
        if (false == $namespace) {
            $namespace = "http://$SERVER_NAME/soap/$serviceName";
        }

        if (false == $endpoint) {
            if ($HTTPS == '1' || $HTTPS == 'on') {
                $SCHEME = 'https';
            } else {
                $SCHEME = 'http';
            }
            $endpoint = "$SCHEME://$SERVER_NAME$SERVER_PORT$SCRIPT_NAME";
        }

        if (false == $schemaTargetNamespace) {
            $schemaTargetNamespace = $namespace;
        }

        $this->wsdl = new wsdl;
        $this->wsdl->serviceName = $serviceName;
        $this->wsdl->endpoint = $endpoint;
        $this->wsdl->namespaces['tns'] = $namespace;
        $this->wsdl->namespaces['soap'] = 'http://schemas.xmlsoap.org/wsdl/soap/';
        $this->wsdl->namespaces['wsdl'] = 'http://schemas.xmlsoap.org/wsdl/';
        if ($schemaTargetNamespace != $namespace) {
            $this->wsdl->namespaces['types'] = $schemaTargetNamespace;
        }
        $this->wsdl->schemas[$schemaTargetNamespace][0] = new nusoap_xmlschema('', '', $this->wsdl->namespaces);
        if ($style == 'document') {
            $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaInfo['elementFormDefault'] = 'qualified';
        }
        $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaTargetNamespace = $schemaTargetNamespace;
        $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/soap/encoding/'][0] = array('location' => '', 'loaded' => true);
        $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/wsdl/'][0] = array('location' => '', 'loaded' => true);
        $this->wsdl->bindings[$serviceName . 'Binding'] = array(
            'name' => $serviceName . 'Binding',
            'style' => $style,
            'transport' => $transport,
            'portType' => $serviceName . 'PortType');
        $this->wsdl->ports[$serviceName . 'Port'] = array(
            'binding' => $serviceName . 'Binding',
            'location' => $endpoint,
            'bindingType' => 'http://schemas.xmlsoap.org/wsdl/soap/');
    }
}